﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Text;
using CommServer.Common;

namespace CommServer.Protocol
{
    public class ProtocolPV
    {
        static ProtocolPV()
        {
            Dictionary<int, ProtocolDataItem> dataItemsV1 = new Dictionary<int, ProtocolDataItem>();
            readDataDefine("ProtocolPV.csv", dataItemsV1);            
            DataItems.Add("1", dataItemsV1);
            Dictionary<int, ProtocolDataItem> dataItemsV2 = new Dictionary<int, ProtocolDataItem>();
            if (File.Exists("ProtocolPV2.csv"))
            {
                readDataDefine("ProtocolPV2.csv", dataItemsV2);
            }
            DataItems.Add("2",dataItemsV2);
        }
        private static void readDataDefine(string fileName,Dictionary<int,ProtocolDataItem> items)
        {
            string[] Lines = File.ReadAllLines(fileName);
            foreach (var Line in Lines)
            {
                string[] Cols = Line.Split(new char[] { ',' });
                if (Line.Length < 7)
                {
                    continue;
                }
                ProtocolDataItem item = new ProtocolDataItem();
                item.dataCode = Convert.ToInt32(Cols[0]);
                item.Des = Cols[1];
                item.DataType = Cols[2];
                item.Mul = Convert.ToDouble(Cols[3]);
                item.Unit = Cols[4];
                item.FieldName = Cols[5];
                item.SMAFieldName = Cols[6];
                items.Add(Convert.ToInt32(Cols[0]), item);
            }
        }
        public static Dictionary<string, Dictionary<int, ProtocolDataItem>> DataItems = new Dictionary<string, Dictionary<int, ProtocolDataItem>>();

        
        public static ProtocolDataItem getDataItemBySMAFieldName(string fieldName)
        {
            foreach(KeyValuePair<int,ProtocolDataItem> pDi in DataItems["1"])
            {
                if(pDi.Value.SMAFieldName.ToLower().Equals(fieldName.ToLower()))
                {
                    return pDi.Value;
                }
            }
            return null;
        }
        public static void FrameToObject(byte[] Frame, Dictionary<string, object> Result)
        {
            switch (Frame[2])
            {
                case 0xF0:
                    break;
                case 0xF1:
                    FrameToObjectF1(Frame, Result);
                    break;
                case 0xF3:
                    FrameToObjectF3(Frame, Result);
                    break;
                case 0xF5:
                    FrameToObjectF5(Frame, Result);
                    break;
                case 0xF6:
                    FrameToObjectF6(Frame, Result);
                    break;
                case 0xFC:
                    FrameToObjectFC(Frame, Result);
                    break;
            }

        }
  		private static void FrameToObjectFC(byte[] frame, Dictionary<string, object> result)
        {//7a 8a FC 00 01 01 crc 1代表成功 0 代表失败
            int operateResult = frame[5];
            result.Add("controlCode", 0xFC);
            result.Add("OperateResult", operateResult);
        }
        private static void FrameToObjectF6(byte[] frame, Dictionary<string, object> result)
        {
            int dataLen = frame[3] * 256 + frame[4];
            byte[] innerFrame = new byte[dataLen];
            Buffer.BlockCopy(frame, 5, innerFrame, 0, innerFrame.Length);

            result.Add("controlCode", 0xF6);
            int dataIndex = 0;
            int total = innerFrame[dataIndex++] * 256 + innerFrame[dataIndex++];
            result.Add("total", total);
            int current = innerFrame[dataIndex++] * 256 + innerFrame[dataIndex++];
            result.Add("current", current);
            int innerDataLen = innerFrame[dataIndex++] * 256 + innerFrame[dataIndex++];
            result.Add("innerDataLen", innerDataLen);
            if (innerDataLen > 0)
            {
                byte[] dataFrame = new byte[innerDataLen];
                Buffer.BlockCopy(innerFrame, dataIndex, dataFrame, 0, innerDataLen);
                result.Add("dataFrame", dataFrame);
            }
        }

        private static void FrameToObjectF5(byte[] frame, Dictionary<string, object> result)
        {
            int dataLen = frame[3] * 256 + frame[4];
            byte[] innerFrame = new byte[dataLen];
            Buffer.BlockCopy(frame, 5, innerFrame, 0, innerFrame.Length);
           
            result.Add("controlCode", 0xF5);
            int dataIndex = 0;
            int total = innerFrame[dataIndex++] * 256 + innerFrame[dataIndex++];
            result.Add("total", total);
            int current= innerFrame[dataIndex++] * 256 + innerFrame[dataIndex++];
            result.Add("current", current);
            int innerDataLen= innerFrame[dataIndex++] * 256 + innerFrame[dataIndex++];
            result.Add("innerDataLen", innerDataLen);
            if(innerDataLen >0)
            {
                byte[] dataFrame = new byte[innerDataLen];
                Buffer.BlockCopy(innerFrame, dataIndex, dataFrame, 0, innerDataLen);
                result.Add("dataFrame", dataFrame);
            }
        }

        private static void FrameToObjectF3(byte[] frame, Dictionary<string, object> result)
        {//7A 8AF3 Len_HLen_LDATA1 ……..DATAN  CRC[2]
            result.Add("controlCode", 0xF3);
            int dataLen = frame[3] * 256 + frame[4];
            byte[] data = new byte[dataLen];
            Buffer.BlockCopy(frame, 5, data, 0, data.Length);
            result.Add("innerFrame", Util.BytesToHex(frame, 5, dataLen));
        }

        private static void FrameToObjectF1(byte[] Frame, Dictionary<string, object> Result)
        {
            int StartPos = 5;
            //ip25字节
            Result.Add("Server", Encoding.ASCII.GetString(Frame, StartPos, 25).Trim(new char[] { '\0' }));
            StartPos += 25;
            //port2字节
            Result.Add("Port", BitConverter.ToUInt16(Frame, StartPos));
            StartPos += 2;
            //APN16字节
            Result.Add("Apn", Encoding.ASCII.GetString(Frame, StartPos, 16).Trim().Trim(new char[] { '\0' }));
            StartPos += 16;
            //UserName16字节
            Result.Add("UserName", Encoding.ASCII.GetString(Frame, StartPos, 16).Trim(new char[] { '\0' }));
            StartPos += 16;
            //UserPwd16字节
            Result.Add("UserPwd", Encoding.ASCII.GetString(Frame, StartPos, 16).Trim(new char[] { '\0' }));
            StartPos += 16;
            //RtuSNNew16字节
            Result.Add("RtuSN", Encoding.ASCII.GetString(Frame, StartPos, 16).Trim(new char[] { '\0' }));
            StartPos += 16;
            //DevPwd32字节
            Result.Add("DevPwd", Encoding.ASCII.GetString(Frame, StartPos, 32).Trim(new char[] { '\0' }));
            StartPos += 32;
            //InvType1字节
            Result.Add("InvType", Frame[StartPos]);
            StartPos += 1;
            //红外表 n*7
            int n = (Frame.Length - StartPos - 2) / 7;
            List<dynamic> Meters = new List<dynamic>();
            for (int i = 0; i < n; i++)
            {
                dynamic Meter = new ExpandoObject();
                string Addr = Util.BytesToHex(Frame, StartPos, 6);
                Meter.Addr = Addr;
                StartPos += 6;
                Meter.Protocol = Frame[StartPos];
                StartPos += 1;
                Meters.Add(Meter);
            }
            Result.Add("Meters", Meters);
        }

        public static byte[] BuildDataFrame(ushort DataType, ushort TypeFlag, byte[] Data)
        {
            List<byte> Frame = new List<byte>();
            //起始符
            Frame.Add(0x69);
            Frame.Add(0x69);
            //包信息版本
            Frame.Add(0x01);
            Frame.Add(0x00);
            //长度
            int Length = 8 + Data.Length;
            Frame.AddRange(BitConverter.GetBytes(Length));
            //协议号
            Frame.Add(1);
            Frame.Add(0);
            //协议主版本
            Frame.Add(1);
            Frame.Add(0);
            //协议次版本
            Frame.Add(0);
            Frame.Add(0);
            //数据类型码
            Frame.AddRange(BitConverter.GetBytes(DataType));
            //类别标识
            Frame.AddRange(BitConverter.GetBytes(TypeFlag));
            //数据
            Frame.AddRange(Data);
            //CRC
            ushort crc = Util.CalCRC16A(Frame, 0, Frame.Count);
            Frame.AddRange(BitConverter.GetBytes(crc));
            return Frame.ToArray();
        }
        public static byte[] BuildDataFrame(ushort DataType, byte[] Data)
        {
            List<byte> Frame = new List<byte>();
            //起始符
            Frame.Add(0x69);
            Frame.Add(0x69);
            //包信息版本
            Frame.Add(0x01);
            Frame.Add(0x00);
            //长度
            int Length = 8 + Data.Length;
            Frame.AddRange(BitConverter.GetBytes(Length));
            //协议号
            Frame.Add(1);
            Frame.Add(0);
            //协议主版本
            Frame.Add(1);
            Frame.Add(0);
            //协议次版本
            Frame.Add(0);
            Frame.Add(0);
            //数据类型码
            Frame.AddRange(BitConverter.GetBytes(DataType));
            //数据
            Frame.AddRange(Data);
            //CRC
            ushort crc = Util.CalCRC16A(Frame, 0, Frame.Count);
            Frame.AddRange(BitConverter.GetBytes(crc));
            return Frame.ToArray();
        }

        public static byte[] BuildMaintainFrame(byte C, byte[] Data, ushort PacketNo = 0, ushort PacketTotal = 0)
        {
            List<byte> Frame = new List<byte>();
            //起始符
            Frame.Add(0x7A);
            Frame.Add(0x8A);
            //控制字段
            Frame.Add(C);
            //数据长度
            Frame.Add((byte)((Data.Length >> 8) & 0xff));
            Frame.Add((byte)(Data.Length & 0xff));

            //数据
            Frame.AddRange(Data);
            //CRC
            ushort crc = Util.CalCRC16A(Frame, 0, Frame.Count);
            Frame.AddRange(BitConverter.GetBytes(crc));
            return Frame.ToArray();
        }
        //false:继续接收数据 true:继续解析数据
        public static bool GetFrame(List<byte> Buffer, out byte[] Frame)
        {

            Frame = null;
            if (Buffer.Count < 2)
            {
                return false;
            }
            //判断包头
            int ProtocolType = 0;
            if (Buffer[0] == 0x69 && Buffer[1] == 0x69)
            {
                ProtocolType = 0;
            }
            else if (Buffer[0] == 0x7A && Buffer[1] == 0x8A)
            {
                ProtocolType = 1;
            }
            else
            {
                Buffer.RemoveAt(0);
                return true;
            }

            int DataLen = 0;
            int FrameLen = 0;
            if (ProtocolType == 0)
            {
                //最小长度不足
                if (Buffer.Count < 18)
                {
                    return false;
                }

                //获取数据长度
                DataLen = Buffer[4] +
                             (Buffer[5] << 8) + (Buffer[6] << 16) + (Buffer[7] << 24);
                FrameLen = DataLen + 10;
            }
            else if (ProtocolType == 1)
            {
                //最小长度不足
                if (Buffer.Count < 7)
                {
                    return false;
                }

                //获取数据长度
                DataLen = (Buffer[3] << 8) + Buffer[4];
                FrameLen = DataLen + 7;

            }

            if (Buffer.Count < FrameLen)
            {
                return false;
            }
            //获取数据(这里简化，不再判断校验)
            Frame = new byte[FrameLen];
            for (var i = 0; i < FrameLen; i++)
            {
                Frame[i] = Buffer[i];
            }
            Buffer.RemoveRange(0, FrameLen);

            if (Buffer.Count == 0)
            {
                return false;
            }
            else
            {
                return true;
            }

        }
    }
    public class ProtocolDataItem
    {
        public int dataCode = -1;
        public string Des = "";
        public string DataType = "uint32";
        public string Unit = "";
        public double Mul = 1;
        public string SMAFieldName = "";
        public string FieldName = "";
    }
}
